home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Skunkware 98
/
Skunkware 98.iso
/
src
/
mail
/
pine3.96.tar.gz
/
pine3.96.tar
/
pine3.96
/
pico
/
msmem.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-04-24
|
28KB
|
1,302 lines
#ifndef MSC_MALLOC
#define WIN31
#define STRICT
#include <windows.h>
#include <toolhelp.h>
#include <stdio.h>
#include "mswin.h"
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*
* Memory allocation routines.
*
*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*
* The plan is to allocate small blocks in the local heap and
* large blocks in the global heap. The intention is to keep
* the number of global allocations to a minimum.
*
* The boundry between small memory and large memory is
* controld by the constant SMALL_MEM_BOUNDRY. Blocks smaller
* than SMALL_MEM_BOUNDRY go into the local heap. This should
* be set large enough to capture the majority of small memory
* blocks in the local heap. But if it is too large, the local
* heap will fill up and we will end up allocating a lot of small
* blocks in the global heap.
*
* Unfortunatly, pine seems to need a large stack. With the
* stack, some global data, and the heap all cramed in to a 64K
* segment we end up with a heap that is smaller than ideal.
* This could be improved by reducing the size of the stack, or
* by moving the heap to a seperate memory block. I did a little
* work on moving the heap, but was not successful. My attepts
* can be seen in the function MemATest().
*
* Currently (7/8/94) I've set the stack to 32K (in pine/makefile.win),
* the heap to 12K, and the small memory boundry to 32 bytes.
* Statistics on pine memory allocation suggest that it would be better
* to have a 25K local heap and a 95 byte small memory boundry.
*
* Statistics on memory use can be gathered by logging memory debugging
* to a file, then running the following awk script:
*
# mem.awk
#
# Looks through log and find blocks that were allocated but not
# freed uses block id numbers, rather than addresses, since this is
# more accurate (and since this is what block ids exist!)
#
# awk may run out of memory if the logfile gets too big. If this
# happens, then another strategy will be needed...
#
BEGIN {
FS = "[ ()]+";
b[0] = 16;
b[1] = 32;
b[2] = 64;
b[3] = 96;
b[4] = 128;
b[5] = 256;
b[6] = 512;
b[7] = 1024;
b[8] = 2048;
b[9] = 4096;
b[10] = 9600;
b[11] = 20000;
b[12] = 40000;
b[13] = 80000;
b[14] = 200000000;
b[15] = 0;
bcount = 15;
for (i = 0; i < bcount; ++i)
c[i] = 0;
maxmem = 0;
maxsmallmem = 0;
allocs = 0;
frees = 0;
globalallocs = 0;
pallocs = 0;
pfrees = 0;
}
{
#print "one", $1, "two", $2, "three", $3, "four ", $4, "five ", $5;
if( $1 == "MemAlloc:" ) {
m[$5] = $0;
++allocs;
if ($9 == 1) ++globalallocs;
for (i = 0; i < bcount; ++i) {
if (b[i] > $7) {
++c[i];
break;
}
}
}
else if( $1 == "MemFree:" ) {
delete m[$5];
++frees;
}
if( $1 == "PageAlloc:" ) {
p[$5] = $0;
++pallocs;
}
else if( $1 == "PageFree:" ) {
delete p[$5];
++pfrees;
}
else if ($1 == "Memory") {
if ($6 > maxmem) maxmem = $6;
}
else if ($1 == "Small") {
if ($7 > maxsmallmem) maxsmallmem = $7;
}
}
END {
for( i in m ) {
print m[i]
}
for (i in p) {
print p[i]
}
cumulative = 0;
for (i = 0; i < bcount; ++i) {
cumulative += c[i];
printf "%9d : %5d (%5d)\n", b[i], c[i], cumulative;
}
print;
print "Max memory use: ", maxmem;
print "Max small memory use: ", maxsmallmem;
print;
print "Local allocations ", allocs - globalallocs;
print "Global allocations ", globalallocs;
print "Total allocations ", allocs;
print "Total memory frees ", frees;
print "Blocks not freed ", allocs - frees;
print;
print "Page allocations ", pallocs;
print "Page frees ", pfrees;
print "Pages not freed ", pallocs - pfrees;
}
*
* Each memory block is assigned a unique id. This is only used
* to match allocations with frees in the debug log.
*/
/*
* SEGHEAP selectes between two different implementations of the memory
* management functions. Defined and it uses a sub allocation scheme.
* Undefined and it comples a direct allocation scheme.
*
* The sub allocation scheme is greatly prefered because it greatly reduces
* the number of global memory allocations.
*/
#define SEGHEAP /* Use the sub allocation scheme. */
#define MEM_DEBUG /* Compile in memory debugging code.*/
#define MEM_DEBUG_LEVEL 8 /* Pine debug level at which memory
* debug messages will be generated.*/
#ifdef MEM_DEBUG
static int MemDebugLevel = 0; /* Doing debugging. */
static FILE *MemDebugFile = NULL; /* File to write to. */
#endif
#ifdef DEBUG
#define LOCAL
#else
#define LOCAL static
#endif
#define GET_SEGMENT(x) (0xffff & ((DWORD)(x) >> 16))
#define GET_OFFSET(x) (0xffff & (DWORD)(x))
#undef malloc
#undef realloc
#undef free
void MemATest (void);
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*
* Standard memory allcation functions.
*
*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void *
malloc (size_t size)
{
return (MemAlloc (size));
}
void __far *
_fmalloc (size_t size)
{
return (MemAlloc (size));
}
void __far *
realloc (void *memblock, size_t size)
{
return (MemRealloc (memblock, size));
}
void __far *
_frealloc (void *memblock, size_t size)
{
return (MemRealloc (memblock, size));
}
void
free (void *memblock)
{
MemFree (memblock);
}
void
_ffree (void *memblock)
{
MemFree (memblock);
}
/*
* Turn on memory debugging and specify file to write to.
*/
void
MemDebug (int debug, FILE *debugFile)
{
#ifdef MEM_DEBUG
if (debugFile == NULL) {
MemDebugLevel = 0;
MemDebugFile = NULL;
}
else {
MemDebugLevel = debug;
MemDebugFile = debugFile;
fprintf (MemDebugFile, "Memory Debuging set on\n");
}
#endif /* MEM_DEBUG */
}
#ifdef SEGHEAP
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*
* SEGHEAP Memory allocation routines.
*
*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*
* This implementation allocates memory in pages then sub alloates from the
* pages. This greatly reduces the number of global memory blocks allocated.
* This code originally written by Stephen Chung and posted to a Usenet news
* group. I've modified them for use in Pine. The author says:
*
*
* Copyright (C) Stephen Chung, 1991-1992. All rights reserved.
*
* Afterwords
* ----------
*
* Theoretically, you are required to obtain special approval from me (because
* I copyrighted these routines) if you want to use them in your programs.
* However, I usually don't really care if you are not using these routines in
* a commercial, shareware etc. product.
*
* Any questions and/or bug fixes, please send email to:
*
* Stephen Chung stephenc@cunixf.cc.columbia.edu
*
* If it bounces, then try schung@cogsci.Berkeley.EDU
*
* Have fun!
*
*/
/*
* The folloing control debugging code for testing out of memory conditions.
* there are to test code.
*
* MEM_ALLOC_LIMT
* Setting this to anything other than zero will limit memory allocation
* to (rougly) that many bytes.
*
* MEM_FAIL_SOON
* Compiles in a function which will cause the memory allocation to fail
* soon after that function is called. this can be used to test
* mem alloc failurs in specific code segments.
*/
#define MEM_ALLOC_LIMIT 0
#define MEM_FAIL_SOON 0
#if MEM_FAIL_SOON
static long MemFailSoonLimit = 0;
#endif
#define MAGIC 0x42022667
#define MAGIC2 0x56743296
typedef struct MemoryStruct {
long int magic;
void far *page;
WORD id;
MemSize size;
BOOL allocated;
struct MemoryStruct far *next, far *prev;
long int magic2;
} MEMHEADER;
typedef struct PageHeaderStruct {
long int magic;
HANDLE handle;
WORD id;
MemSize size;
MemSize used;
MemSize overhead;
MEMHEADER far *data, far *empty;
struct PageHeaderStruct far *next, far *prev;
long int magic2;
} MEMPAGEHEADER;
typedef struct {
MEMPAGEHEADER far *pages;
int nr_pages;
} MAINMEMHEADER;
#define PAGESIZE (6 * 1024)
#define USEABLESIZE (PAGESIZE - sizeof(MEMPAGEHEADER) - sizeof(MEMHEADER))
LOCAL MAINMEMHEADER MemHeader = { NULL, 0 };
LOCAL WORD MemID = 0; /* Keep track of ID. */
LOCAL WORD PageID = 0;
LOCAL unsigned long MemInUse = 0; /* Total bytes in use. */
LOCAL unsigned long MemInUseMax = 0; /* max in use at one time. */
LOCAL unsigned long PageMemInUse = 0;
LOCAL unsigned long PageMemInUseMax = 0;
static MEMPAGEHEADER far *
AddPage(MemSize n)
{
void far *cp;
MEMHEADER far *mp;
MEMPAGEHEADER far *p;
HANDLE handle = NULL;
#if MEM_ALLOC_LIMIT
if (n + PageMemInUse > MEM_ALLOC_LIMIT) {
MessageBox (NULL, "PageAlloc: Above preset limit, allocation fails",
"Out Of Memory", MB_ICONSTOP | MB_OK);
return (NULL);
}
#endif
handle = GlobalAlloc(GHND, n);
if (handle == NULL) {
#ifdef MEM_DEBUG
if (MemDebugLevel >= 1)
fprintf (MemDebugFile, "***\n*** Out of memory: allocating %d bytes\n***\n", n);
#endif
return (NULL);
}
if (MemHeader.pages == NULL || MemHeader.nr_pages <= 0) {
p = MemHeader.pages = (MEMPAGEHEADER far *) GlobalLock(handle);
p->prev = NULL;
} else {
for (p = MemHeader.pages; p->next != NULL; p = p->next);
p->next = (MEMPAGEHEADER far *) GlobalLock(handle);
p->next->prev = p;
p = p->next;
}
p->magic = MAGIC;
p->handle = handle;
p->next = NULL;
p->id = PageID++;
p->size = n;
p->used = 0;
p->overhead = sizeof(MEMPAGEHEADER) + sizeof(MEMHEADER);
p->magic2 = MAGIC2;
cp = ((char far *) p) + sizeof(MEMPAGEHEADER);
mp = (MEMHEADER far *) cp;
p->data = p->empty = mp;
mp->magic = 0L;
mp->magic2 = 0L;
mp->allocated = FALSE;
mp->page = p;
mp->size = p->size - p->overhead;
mp->next = mp->prev = NULL;
MemHeader.nr_pages++;
#ifdef MEM_DEBUG
if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
fprintf (MemDebugFile, "PageAlloc: addr(%lx) id(%u) size(%ld) global(%d)\n",
p, p->id, (long)n, 1);
fflush (MemDebugFile);
}
#endif /* MEM_DEBUG */
PageMemInUse += n;
if (PageMemInUse > PageMemInUseMax)
PageMemInUseMax = PageMemInUse;
return (p);
}
static void
DeletePage (MEMPAGEHEADER far *p)
{
#ifdef MEM_DEBUG
/* Deubgging info... */
if (MemDebugLevel >= 4) {
if (PageMemInUse == PageMemInUseMax)
fprintf (MemDebugFile, "Page usage is up to %lu\n", PageMemInUseMax);
}
if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
fprintf (MemDebugFile, "PageFree: addr(%lx) id(%u)\n",
p, p->id);
fflush (MemDebugFile);
}
#endif /* MEM_DEBUG */
PageMemInUse -= p->size;
if (p->next == NULL && p->prev == NULL) {
MemHeader.pages = NULL;
MemHeader.nr_pages = 0;
GlobalUnlock (p->handle);
GlobalFree (p->handle);
} else {
if (p == MemHeader.pages) MemHeader.pages = p->next;
MemHeader.nr_pages--;
if (p->prev != NULL) p->prev->next = p->next;
if (p->next != NULL) p->next->prev = p->prev;
GlobalUnlock (p->handle);
GlobalFree (p->handle);
}
}
/*
* Segmented heap memory allocation.
*/
MemPtr
_MemAlloc (MemSize n, char __far * file, int line)
{
MEMPAGEHEADER far *p;
MEMHEADER far *mp;
char far *cp;
if (n >= 65535) {
assert (n < 65535);
goto AllocFail;
}
#if MEM_FAIL_SOON
if (MemFailSoonLimit != 0 && MemFailSoonLimit < MemInUse + n) {
MessageBox (NULL, "MemAlloc: Told to fail here, allocation fails",
"Out Of Memory", MB_ICONSTOP | MB_OK);
return (NULL);
}
#endif
/* Larger than page size? */
if (n > USEABLESIZE) {
p = AddPage(n + sizeof(MEMPAGEHEADER) + sizeof(MEMHEADER));
if (p == NULL)
goto AllocFail;
mp = p->data;
mp->magic = MAGIC;
mp->magic2 = MAGIC2;
mp->id = MemID++;
mp->allocated = TRUE;
p->used = n;
p->empty = NULL;
cp = ((char far *) mp) + sizeof(MEMHEADER);
#ifdef MEM_DEBUG
if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
fprintf (MemDebugFile, "MemAlloc: addr(%lx) id(%u) size(%ld) global(%d)\n",
cp, mp->id, (long)n, 0);
fflush (MemDebugFile);
}
#endif /* MEM_DEBUG */
MemInUse += n;
if (MemInUse > MemInUseMax)
MemInUseMax = MemInUse;
return ((MemPtr) cp);
}
/* Search for the hole */
for (p = MemHeader.pages; p != NULL; p = p->next) {
/* Scan the chains */
if (p->size - p->used - p->overhead <= 0) continue;
if (p->empty == NULL) continue;
for (mp = p->empty; mp != NULL; mp = mp->next) {
if (!mp->allocated && mp->size >= n) break;
}
if (mp != NULL) break;
}
/* New page needed? */
if (p == NULL) {
p = AddPage(PAGESIZE);
if (p == NULL)
goto AllocFail;
mp = p->data;
}
/* Do we need to break it up? */
if (mp->size - n > sizeof(MEMHEADER)) {
MEMHEADER far *mp2;
cp = ((char far *) mp) + n + sizeof(MEMHEADER);
mp2 = (MEMHEADER far *) cp;
mp2->magic = 0L;
mp2->magic2 = 0L;
mp2->allocated = FALSE;
mp2->page = p;
mp2->size = mp->size - n - sizeof(MEMHEADER);
mp2->next = mp->next;
mp2->prev = mp;
if (mp->next != NULL) mp->next->prev = mp2;
mp->next = mp2;
p->overhead += sizeof(MEMHEADER);
mp->size = n;
}
mp->magic = MAGIC;
mp->magic2 = MAGIC2;
mp->allocated = TRUE;
mp->id = MemID++;
p->used += n;
cp = ((char far *) mp) + sizeof(MEMHEADER);
/* Debugging info... */
#ifdef MEM_DEBUG
if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
fprintf (MemDebugFile, "MemAlloc: addr(%lx) id(%u) size(%ld) global(%d)\n",
cp, mp->id, (long)n, 0);
fflush (MemDebugFile);
}
#endif /* MEM_DEBUG */
MemInUse += n;
if (MemInUse > MemInUseMax)
MemInUseMax = MemInUse;
/* Search for the next empty hole */
for (; mp != NULL; mp = mp->next) {
if (!mp->allocated && mp->size > 0) break;
}
p->empty = mp;
return ((MemPtr) cp);
AllocFail:
#if 0
assert (FALSE /* Memory allocation failed! */);*/
#endif
return (NULL);
}
/*
* Segmented heap memory free.
*/
int
_MemFree (MemPtr vp, char __far *file, int line)
{
MEMPAGEHEADER far *p;
MEMHEADER far *mp, far *mp2;
char far *cp;
if (vp == NULL)
return (0);
cp = ((char far *) vp) - sizeof(MEMHEADER);
mp = (MEMHEADER far *) cp;
if (mp->magic != MAGIC || mp->magic2 != MAGIC2|| !mp->allocated) {
assert (mp->magic == MAGIC);
assert (mp->magic2 == MAGIC2);
assert (mp->allocated);
return (-1);
}
#ifdef MEM_DEBUG
/* Deubgging info... */
if (MemDebugLevel >= 4) {
if (MemInUse == MemInUseMax)
fprintf (MemDebugFile, "Memory usage is up to %lu\n", MemInUseMax);
}
if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
fprintf (MemDebugFile, "MemFree: addr(%lx) id(%u)\n", vp, mp->id);
fflush (MemDebugFile);
}
#endif /* MEM_DEBUG */
MemInUse -= mp->size;
p = (MEMPAGEHEADER far *) mp->page;
p->used -= mp->size;
mp->magic = 0L;
mp->magic2 = 0L;
mp->allocated = FALSE;
/* Merge? */
mp2 = mp->prev;
if (mp2 != NULL && !mp2->allocated) {
mp2->next = mp->next;
if (mp->next != NULL) mp->next->prev = mp2;
mp2->size += mp->size + sizeof(MEMHEADER);
p->overhead -= sizeof(MEMHEADER);
mp = mp2;
}
mp2 = mp->next;
if (mp2 != NULL && !mp2->allocated) {
mp->next = mp2->next;
if (mp2->next != NULL)
mp2->next->prev = mp;
mp->size += mp2->size + sizeof(MEMHEADER);
p->overhead -= sizeof(MEMHEADER);
}
if (mp->prev == NULL && mp->next == NULL) {
DeletePage(p);
} else {
if (p->empty == NULL || mp < p->empty) p->empty = mp;
}
return (0);
}
MemPtr
_MemRealloc (MemPtr p, MemSize n, char __far *file, int line)
{
MEMHEADER far *mp;
char far *cp;
if (p != NULL) {
/* Block already large enough? */
cp = ((char far *) p) - sizeof(MEMHEADER);
mp = (MEMHEADER far *) cp;
if (mp->magic != MAGIC || mp->magic2 != MAGIC2) {
assert (mp->magic == MAGIC);
assert (mp->magic2 == MAGIC2);
return (p);
}
if (mp->size >= n) return (p); /* No need to do anything */
}
/* Else swap to another block */
cp = MemAlloc (n);
if (cp == NULL)
return (NULL);
if (p != NULL) {
_fmemcpy(cp, p, (size_t)((mp->size >= n) ? n : mp->size));
MemFree (p);
}
return ((void far *) cp);
}
void
MemFailSoon (MemSize n)
{
#if MEM_FAIL_SOON
MemFailSoonLimit = MemInUse + n;
#ifdef MEM_DEBUG
if (MemDebugLevel >= 1) {
fprintf (MemDebugFile,
"MemFailSoon: Fail when allocation increases by %ld (Max %ld)\n",
n, MemFailSoonLimit);
}
#endif
#endif
}
MemSize
MemBlkSize (MemPtr p)
{
MEMHEADER far *mp;
char far *cp;
if (p == NULL) return (0);
cp = ((char far *) p) - sizeof(MEMHEADER);
mp = (MEMHEADER far *) cp;
if (mp->magic != MAGIC || mp->magic2 != MAGIC2) {
assert (mp->magic == MAGIC);
assert (mp->magic2 == MAGIC2);
return (0);
}
return (mp->size);
}
#if 0
MemPtr
MemDup (void far *p)
{
unsigned int len;
void far *p1;
len = MgetBlkSize (p);
p1 = MemAlloc (len);
if (p1 != NULL)
_fmemcpy(p1, p, len);
return (p1);
}
void
MemoryStatistics (long int *allocated, long int *used, long int *overhead)
{
MEMPAGEHEADER far *p;
*allocated = *used = *overhead = 0L;
for (p = MemHeader.pages; p != NULL; p = p->next) {
*allocated += p->size;
*used += p->used;
*overhead += p->overhead;
}
}
#endif
void
MemFreeAll (void)
{
MEMPAGEHEADER far *p, far *p1;
for (p = MemHeader.pages; p != NULL; ) {
p1 = p->next;
GlobalUnlock (p->handle);
GlobalFree (p->handle);
p = p1;
}
MemHeader.pages = NULL;
MemHeader.nr_pages = 0;
}
#ifdef MEM_DEBUG
/* For debugging purposes... not very pretty */
void PrintMemoryChains(void)
{
MEMPAGEHEADER far *p;
MEMHEADER far *mp;
char far *cp;
char buffer[100];
/* Block already large enough? */
for (p = MemHeader.pages; p != NULL; p = p->next) {
for (mp = p->data; mp != NULL; mp = mp->next) {
fprintf (MemDebugFile, "%Fp | %u | %s", mp, mp->size, mp->allocated ? "Alloc" : "Free");
}
}
}
#endif /* DEBUG */
#else /* !SEGHEAP. Old version, not used. */
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*
* Direct memory allocation
*
*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*
* This following implementation allocates large memory blocks directly
* from global memory and small memory blocks from the local heap. The
* problem with this method is that pine's local heap is quite small
* so most of the memory ends up comming from the global heap.
*/
#define GUARD_LOW0 0xbbbb
#define GUARD_LOW 0x9999
#define GUARD_HIGH 0xaaaaaaaa
#define SMALL_MEM_BOUNDRY 32
#define HEAP_SIZE 32000
/* Memory block header. Placed at beginning of each allocated block. */
typedef struct { /*size len */
WORD guard0; /* 00 - 02 */
HGLOBAL handle; /* 02 - 02 */
short globalBlk; /* 04 - 02 */
MemSize size; /* 06 - 04 */
WORD id; /* 0A - 02 */
WORD guard1; /* 0C - 02 */
} MemBlk; /* Total size: 0E */
typedef MemBlk __far * MemBlkPtr;
/* Memory high guard tailer. Placed at end of each allocated block. */
typedef struct {
unsigned long guard1;
} MemHighGuard;
typedef MemHighGuard __far *MemHighGuardPtr;
/*
* Memory allocation globals.
*/
LOCAL WORD MemID = 0; /* Keep track of ID. */
LOCAL unsigned long MemLocalFails = 0;
LOCAL BOOL MemLocalFull = FALSE; /* True when local heap full*/
#ifdef MEM_DEBUG
LOCAL unsigned long MemInUse = 0; /* Total bytes in use. */
LOCAL unsigned long MemInUseMax = 0; /* max in use at one time. */
LOCAL unsigned long SmallMemInUse = 0;
LOCAL unsigned long SmallMemInUseMax = 0;
#endif /* MEM_DEBUG */
/*
* Allocate a memory block.
* The file and line indicate where we are called from (for debugging)
* but in pine these mostly point to a bottel neck routine and are
* useless.
*/
MemPtr
_MemAlloc (MemSize size, char __far * file, int line)
{
MemBlkPtr pBlk;
MemHighGuardPtr pHG;
HGLOBAL hBlk;
HLOCAL hLBlk;
UINT totalSize;
BYTE __far * pb;
assert (size <= MEM_BLOCK_SIZE_MAX);
/*
* Calculate total size we need to allocate.
*/
totalSize = (UINT)size + sizeof (MemBlk) + sizeof (MemHighGuard);
pBlk = NULL;
/*
* If it's a small block and the local heap is not full, try
* allocating from the local heap.
*/
if (size <= SMALL_MEM_BOUNDRY && !MemLocalFull) {
/* Allocate block from local storage. */
hLBlk = LocalAlloc (LMEM_MOVEABLE, totalSize);
if (hLBlk != NULL) {
/* Lock block and dereference. */
pBlk = (MemBlkPtr) LocalLock (hLBlk);
if (pBlk != NULL) {
pBlk->handle = hLBlk;
pBlk->globalBlk = FALSE;
}
else
LocalFree (hLBlk);
}
else {
++MemLocalFails;
MemLocalFull = TRUE;
#ifdef MEM_DEBUG
if (MemDebugLevel >= MEM_DEBUG_LEVEL)
fprintf (MemDebugFile, "Local Memory alloc failed, %lu fails, %lu bytes in use\n",
MemLocalFails, SmallMemInUse);
#endif
}
}
/*
* If it is a large block, or local alloc failed, we allocate from
* global space.
*/
if (pBlk == NULL) {
/* Allocate block from global storage. */
hBlk = GlobalAlloc (GMEM_MOVEABLE, totalSize);
if (hBlk == NULL)
return (NULL);
/* Lock block and dereference. */
pBlk = (MemBlkPtr) GlobalLock (hBlk);
if (pBlk == NULL) {
GlobalFree (hBlk);
return (NULL);
}
pBlk->handle = hBlk;
pBlk->globalBlk = TRUE;
}
/* Fill rest of header. */
pBlk->guard0 = GUARD_LOW0;
pBlk->size = size;
pBlk->id = ++MemID;
pBlk->guard1 = GUARD_LOW;
/* Find address that will be returned to caller. */
pb = (BYTE __far *) (pBlk + 1);
/* Find high guard and fill. */
pHG = (MemHighGuardPtr) (pb + size);
pHG->guard1 = GUARD_HIGH;
/* Debugging info... */
#ifdef MEM_DEBUG
if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
if( !file ) file = "??";
fprintf (MemDebugFile, "MemAlloc: addr(%lx) id(%u) size(%ld) global(%d)\n",
pb, pBlk->id, (long)size, pBlk->globalBlk);
fflush (MemDebugFile);
}
MemInUse += totalSize;
if (MemInUse > MemInUseMax)
MemInUseMax = MemInUse;
if (size <= SMALL_MEM_BOUNDRY) {
SmallMemInUse += totalSize;
if (SmallMemInUse > SmallMemInUseMax)
SmallMemInUseMax = SmallMemInUse;
}
#endif /* MEM_DEBUG */
return ((MemPtr) pb);
}
/*
* Free a block.
*/
int
_MemFree (MemPtr block, char __far *file, int line)
{
MemBlkPtr pBlk;
MemHighGuardPtr pHG;
HGLOBAL hBlk;
HLOCAL hLBlk;
BOOL brc;
UINT totalSize;
if (block == NULL)
return (0);
/* Find header and high guard and check them. */
pBlk = ((MemBlkPtr)block) - 1;
pHG = (MemHighGuardPtr) ((char __far *)block + pBlk->size);
totalSize = pBlk->size + sizeof (MemBlk) + sizeof (MemHighGuard);
/* If these changed them someone wrote where the should not have. */
assert (pBlk->guard0 == GUARD_LOW0);
assert (pBlk->guard1 == GUARD_LOW);
assert (pHG->guard1 == GUARD_HIGH);
#ifdef MEM_DEBUG
/* Deubgging info... */
if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
if (pBlk->size <= SMALL_MEM_BOUNDRY &&
SmallMemInUse == SmallMemInUseMax)
fprintf (MemDebugFile, "Small memory usage is up to %lu\n", SmallMemInUseMax);
if (MemInUse == MemInUseMax)
fprintf (MemDebugFile, "Memory usage is up to %lu\n", MemInUseMax);
}
MemInUse -= totalSize;
if (pBlk->size <= SMALL_MEM_BOUNDRY)
SmallMemInUse -= totalSize;
if (MemDebugLevel >= MEM_DEBUG_LEVEL) {
fprintf (MemDebugFile, "MemFree: addr(%lx) id(%u)\n",
block, pBlk->id);
fflush (MemDebugFile);
}
#endif /* MEM_DEBUG */
/*
* Header indicates which block it came from
*/
if (!pBlk->globalBlk) {
/* Unlock block */
hLBlk = pBlk->handle;
brc = LocalUnlock (hLBlk);
assert (!brc);
/* And free block. */
hLBlk = LocalFree (hLBlk);
assert (hLBlk == NULL);
MemLocalFull = FALSE;
}
else {
/* Unlock block */
hBlk = pBlk->handle;
brc = GlobalUnlock (hBlk);
assert (!brc);
/* And free block. */
hBlk = GlobalFree (hBlk);
assert (hBlk == NULL);
}
return (0);
}
/*
* Reallocate a memory block. Simplistic approach.
*/
MemPtr
_MemRealloc (MemPtr block, MemSize size, char __far * file, int line)
{
MemPtr newBlock;
newBlock = MemAlloc (size);
if (newBlock == NULL)
return (NULL);
if (block != NULL) {
_fmemcpy (newBlock, block , (size_t)MIN (size, MemBlkSize (block)));
MemFree (block);
}
return (newBlock);
}
/*
* Return the size of a memory block
*/
MemSize
MemBlkSize (MemPtr block)
{
MemBlkPtr pBlk;
if (block == NULL) return (0);
pBlk = ((MemBlkPtr)block) - 1;
assert (pBlk->guard1 == GUARD_LOW);
return (pBlk->size);
}
#ifdef MEM_DEBUG
struct testblock {
struct testblock __far * prev;
HLOCAL h;
};
void
MemATest (void)
{
void __near *n;
struct testblock __far *p;
struct testblock __far *pnew;
HLOCAL hl;
int bcount;
UINT segment, start, end;
void __far *f;
HGLOBAL hg;
DWORD dw;
LOCALINFO li;
UINT DataSeg;
#if 0
hg = GlobalAlloc (GMEM_FIXED, HEAP_SIZE); /* Allocate global block */
if (hg == NULL)
return;
f = GlobalLock (hg); /* Lock and get pointer. */
if (f == NULL)
goto Fail1;
segment = (UINT) GET_SEGMENT (f); /* Get segment and offsets. */
start = (UINT) GET_OFFSET (f);
end = start + HEAP_SIZE - 1;
start += 16;
if (!LocalInit (segment, start, end)) /* Init it as the local heap*/
goto Fail2;
#endif
#if 0
__asm MOV DataSeg,DS; /* Get current DS. */
__asm MOV DS,segment; /* Set DS to new heap. */
hl = LocalAlloc (0, SMALL_MEM_BOUNDRY); /* Now allocate something. */
__asm MOV DS,DataSeg; /* Restore DS. */
if (hl == NULL)
return;
n = LocalLock (hl); /* Find where it is. */
f = (void __far *)n;
segment = GET_SEGMENT(f); /* What Segment. */
dw = GlobalHandle (segment);
hg = (HGLOBAL) (dw & 0xffff);
if (hg == NULL)
return;
li.dwSize = sizeof (li); /* What size. */
if (!LocalInfo (&li, hg))
return;
dw = GlobalSize (hg);
f = GlobalLock (hg);
GlobalUnlock (hg);
LocalUnlock (hl);
LocalFree (hl);
#endif
p = NULL;
pnew = NULL;
bcount = 0;
do {
hl = LocalAlloc (0, SMALL_MEM_BOUNDRY);
if (hl != NULL) {
++bcount;
n = LocalLock (hl);
pnew = (struct testblock __far*) n;
pnew->h = hl;
pnew->prev = p;
p = pnew;
}
} while (hl != NULL);
if (MemDebugFile != NULL)
fprintf (MemDebugFile, "Allocated %d blocks of size %d\n",
bcount, SMALL_MEM_BOUNDRY);
while (p != NULL) {
pnew = p->prev;
hl = p->h;
LocalUnlock (hl);
LocalFree (hl);
p = pnew;
}
fflush (MemDebugFile);
#if 0
Fail2: GlobalUnlock (hg);
Fail1: GlobalFree (hg);
#endif
return;
}
#endif /* MEM_DEBUG */
#endif /* ifdef SEGHEAP */
#endif /* MSC_MALLOC */